﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    /**
     一、委托的概念
            C#中的委托（Delegate）是一种引用类型，是把方法变成一种引用类型的方式。
            委托适用于定义事件和回调方法，所有的委托都派生自System.Delegate类，关键字是delegate.
            使用委托和使用类一样，都有定义声明和实例化两部。
            委托是一种定义方法签名的类型(方法签名包括返回值和参数类型)。
            当实例化委托时，您可以将其实例与任何具有兼容签名的方法相关联。 您可以通过委托实例调用方法。
            委托用于将方法作为参数传递给其他方法。
            事件处理程序就是通过委托调用的方法。
            您可以创建一个自定义方法，当发生特定事件时某个类（例如 Windows 控件）就可以调用您的方法。 下面的示例演示了一个委托声明：
    **/



    ///声明委托
    //声明一个无返回值类型的委托
    delegate void Delegate1();
    //声明一个无返回值，有一个参数的委托
    delegate void Delegate2(int a);
    //声明一个有返回值有参数的委托
    delegate string Delegate3(string a, string b);


    /*
        在概念上"委托"和 "类" 、"记录"、"枚举"是一个东西，是一个概念性范围。

            public class A
            {

            }
            在使用 class 关键字声明"A类型的类"

            public enum Sex
            {
                  男,
                  女
            }
            是在使用enum关键字声明一个"Sex类型的枚举"

            record Student(int studentNo,string studentName);
            是在使用record关键字创建"Student类型的记录"

        同样：
            delegate string Delegate5(strintg a,string b);
            是在使用delegate关键字，在声明一个"Delegate5类型的委托"

        类的概念是 ：     现实世界中一类物体的抽象
        记录的概念是：    对同一对象的不同维度的描述的抽象(比如对所有"早上迟到行为"的记录,"早上迟到行为"被认为是对象，所有对其描述的抽象成为"早上迟到行为类型记录")
        枚举概念：        若干子部分组成一个整体描述一件对象的一个属性，这个属性的当前状态，必然是这个"若干子部分"中的某一个。
        委托的概念：      拥有相同返回值类型、参数个数、参数名称的方法的抽象。(拥有相同方法签名的方法的抽象)；


        委托对象，有点类似于js中的函数表达式  let aa =()=>{console.log('你是大笨蛋！')};
        只不过，C#的委托，在委托对象的概念之上，又有了"委托类型"的概念，
        C#中我们所说的"声明委托"，就是在定义一个"委托类型"：

        deletegate string Haha(string reason);
        声明一个，返回string类型，并且有一个名为"reason"的string类型参数的委托。

        C#中的匿名函数 ()=>{ }  本质上就是"匿名委托" 
    */



    /// <summary>
    /// 自定义的类
    /// </summary>
    public class People
    {
        //定义一个没有返回值，没有参数的方法
        public void PeopleMethod()
        {

        }

        //定义一个有参数，没有返回值的方法
        public void PeopleMethod2(int a)
        {

        }

        //定义一个有参数，有返回值的方法
        public string PeopleMethod3(string a, string b)
        {
            return a + b;
        }
    }


    // 访问修饰符internal在程序集内可访问
    internal class DelegateTest
    {
        public void test2()
        {

        }

        public void test()
        {
            //实例化People类
            People people = new People();

            int a = 0;

            //委托订阅匿名方法
            Delegate1 del1 = delegate
            {
                int b = a++;//匿名方法外部定义的变量，无需使用参数传入，可直接访问
                Console.WriteLine("委托定义匿名方法");
            };
            //委托订阅Lamda表达式
            //Lamda表达式本质就是匿名方法，不过是为了简化代码(Lamda表达式一般和委托一起用)
            Delegate1 del2 = () =>
            {
                int b = a++;//Lamda表达式外部定义的似有变量，无需使用参数传入，可直接访问
                Console.WriteLine("委托订阅Lamda表达式");
            };

            //匿名对象
            //没有名称的对象，叫匿名对象
            var obj = new { name = 1, price = 33, nickname = "余冰" };
            obj.name;
            obj.price;
            obj.nickname;


            //订阅当前对象的方法
            Delegate1 del3 = new Delegate1(test2);

            //创建一个无返回值无参数的委托对象
            Delegate1 delegate1 = new Delegate1(people.PeopleMethod);
            //通过委托调用people的PeopleMethod方法
            delegate1();

            //创建一个有参数无返回值的委托对象
            Delegate2 delegate2 = new Delegate2(people.PeopleMethod2);
            //通过委托对象调用people对象的PeopleMethod方法
            delegate2(10);//调用时根据要求传入实参

            //创建一个带有两个参数的委托对象
            Delegate3 delegate3 = new Delegate3(people.PeopleMethod3);
            //通过委托调用方法
            delegate3("aaa", "bb");
        }
    }



    //2、使用委托作为，函数参数

    public delegate void Delegate4();//声明委托

    //自定义的类
    public class Animal
    {
        //定义函数参数为 Delegate4类型的委托
        public static void Method(Delegate4 dele)
        {
            dele();//直接通过委托调用方法
        }
    }

    //在此类的函数里声明委托，实例化后作为参数调用Animal类的Method函数
    //委托作为参数传入函数，实现了通过编程让函数执行不同的逻辑（可用委托实现在上层函数中根据情况执行不同的逻辑）
    public class DoSomething
    {
        public void method()
        {
            People people = new People();
            Delegate4 delegate4 = new Delegate4(people.PeopleMethod);//实例化委托
            Animal.Method(delegate4);//委托作为参数传入方法
        }
    }




    //3、定义泛型委托
    //    泛型委托，通过给定类型参数（“<>”）来规定委托的返回值和参数的类型，也就是说泛型委托允许了模糊定义委托的方法签名
    //    例子如下： 


    //声明一个带返回值和参数的泛型委托
    delegate T DelegateFanXing<T, W, V>(W a, V b);

    class FanXingDelegateTest
    {
        public string FXDelegate()
        {
            People people = new People();
            //创建泛型委托实例并初始化
            DelegateFanXing<string, string, string> delegateFanXing = new DelegateFanXing<string, string, string>(people.PeopleMethod3);
            return test(delegateFanXing);//将泛型委托为参数，调用test方法
        }

        public string test(DelegateFanXing<string, string, string> delegateFanXing)
        {
            //直接执行委托
            return delegateFanXing("aa", "bb");
        }
            }

    //  4、委托作为类的字段
    //      平时用委托的一般方式，先声明委托，然后再去使用，有点麻烦，.net中有已经定义好的委托类型Action 和 Func，可以拿来直接用
    //      委托作为字段时，建议使用 Func和Action两个.net内置的委托类型
    //      简单说就是  有返回值就用Func委托类型，无返回值就用Action委托类型
    //      (1). Action至少0个参数，至多16个参数，没有返回值的泛型委托
    //      (2).Func 至少0个参数，至多16个参数，必须有返回值的泛型委托

    /**
            记住无返回值就用Action，有返回值就用Func

            Action：无参数无返回值委托。

            Action<T>：泛型委托，无返回值，根据输入参数的个数不同有十六个重载。
            Action<T,T1>:2个参数的委托
            Action<T,T2,T3>:3个参数的委托
            Action<T,........>最多可以有16个参数的类型参数

           
            Func<out T>：无输入参数，有返回值。

            Func<in T,out T>：有输入参数，有返回值，根据输入参数个数不同，有十六个重载。
            
            Action和Func中可以使用Lambda和匿名方法处理方法体内逻辑。
    **/
        public void test10()
        {
            //无参无返回
            Action a1 = () => { int n = 1; };
            //1参，无返回
            Action<int> a2 = i => { int h = 0; };
            //2参，无返回值
            Action<int, string> a3 = (i, j) => { int kl = 0; };
            //3参，无返回值
            Action<string, int, double> a4 = (i, j, k) => { i = "ada"; j = 2; k = "dafa"; };

            //类型参数列表中最后一个类型参数，为返回值类型
            //out 标志的参数为输出参数
            //无参，有返回值
            Func<int> f1 = () => { return 1; };
            //返回int ,1个string类型参数
            Func<string, int> f2 = i => { if (i == "") return 1; };


        }



    //5、多播
    //          委托具有一个非常重要的属性，那就是通过"+"运算符将多个方法对象分配给一个方法实例，同时还可以使用"-"运算符从委托中移除已分配的方法对象
    //          当委托被调用时会依次调用列表中的委托
    internal class Program
    {
        Action action;
        Func<int> func;
        static void Test()
        {
            Program program = new Program();
            //多播
            program.action = Method1;
            program.action += Method2;

            program.action -= Method1;

            program.action(); //多播具有返回值的，只会得到最后一个方法的结果


            program.func = Method3;
            Console.WriteLine(program.func());
            Console.ReadKey();
        }
        public static void Method1()
        {
            Console.WriteLine(1);
        }
        public static void Method2()
        {
            Console.WriteLine(2);
        }

        public static int Method3()
        {
            return 10;
        }
        public static int Method4()
        {
            return 20;
        }
    }


    // 6、典型使用场景：  实现C#中的回调:
            public delegate int Calculator(int a, int b);

            public class MathHelper
            {
                public int Compute(int a, int b, Calculator calculator)
                {
                    return calculator(a, b);
                }
            }

            public class AService
            {
                public int a = 0;

                public int b = 0;

                public int XY(int x,int y)
                { 
                   return x + y;
                }

                // 实现回调
                public void AAA()
                { 
                    var mathHelper = new MathHelper();
                    int res = mathHelper.Compute(a, b, XY);// 调用MarhHelper的Computer时，实现回调
                }
            } 
}
